#ifndef Vec_HPP_
# define Vec_HPP_

# include <iostream>
# include <math.h>

#ifdef _MSC_VER
// Disable float -> double warning.
# pragma warning(disable: 4305)
# pragma warning(disable: 4244)
#endif

template <unsigned int N, typename T>
class		Vec
{
public:
	Vec()
	{
	}

	Vec(T x, T y)
	{
		set(x, y);
	}

	Vec(T x, T y, T z)
	{
		set(x, y, z);
	}

	void			init(const T value = 0)
	{
		for (unsigned int c = 0; c < N; c++)
		{
			_vec[c] = value;
		}
	}

	T*				get() const
	{
		return (T*)_vec;
	}

	void			set(T x, T y)
	{
		_vec[0] = x;
		_vec[1] = y;
	}

	void			set(T x, T y, T z)
	{
		_vec[0] = x;
		_vec[1] = y;
		_vec[2] = z;
	}

	void			set(Vec const& newValue)
	{
		for (unsigned int c = 0; c < N; c++)
		{
			_vec[c] = newValue._vec[c];
		}
	}

	void			display() const
	{
		for (unsigned int c = 0; c < N; c++)
		{
			std::cout << _vec[c] << std::endl;
		}
	}

	T				operator[](const unsigned int value) const
	{
		return _vec[value];
	}

	T&				operator[](const unsigned int value)
	{
		return _vec[value];
	}

	Vec&			operator=(Vec const& right)
	{
		for (unsigned int c = 0; c < N; c++)
		{
			_vec[c] = right._vec[c];
		}
		return *this;
	}

	Vec				operator+(Vec const& right)
	{
		Vec			result;

		for (unsigned int c = 0; c < N; c++)
		{
			result._vec[c] = _vec[c] + right._vec[c];
		}
		return result;
	}

	Vec&			operator+=(Vec const& right)
	{
		*this = *this + right;
		return *this;
	}

	Vec				operator-(Vec const& right)
	{
		Vec			result;

		for (unsigned int c = 0; c < N; c++)
		{
			result._vec[c] = _vec[c] - right._vec[c];
		}
		return result;
	}

	Vec&			operator-=(Vec const& right)
	{
		*this = *this - right;
		return *this;
	}

	Vec				operator*(Vec const& right)
	{
		Vec			result;

		for (unsigned int c = 0; c < N; c++)
		{
			result._vec[c] = _vec[c] * right._vec[c];
		}
		return result;
	}

    Vec             operator*(T const& value)
    {
        Vec         result;

        for (unsigned int c = 0; c < N; c++)
        {
            result._vec[c] = _vec[c] * value;
        }
        return result;
    }

	Vec&			operator*=(Vec const& right)
	{
		*this = *this * right;
		return *this;
	}

	Vec				operator/(Vec const& right)
	{
		Vec			result;

		for (unsigned int c = 0; c < N; c++)
		{
			result._vec[c] = _vec[c] / right._vec[c];
		}
		return result;
	}

	Vec&			operator/=(Vec const& right)
	{
		*this = *this / right;
		return *this;
	}

	inline double	length() const
	{
		double	result = 0.0;

		for (unsigned int c = 0; c < N; c++)
		{
			result += _vec[c] * _vec[c];
		}
		return sqrt(result);
	}

	inline double	dot(Vec const& right) const
	{
		double		result = 0.0;

		for (unsigned int c = 0; c < N; c++)
		{
			result += _vec[c] * right._vec[c];
		}
		return result;
	}

    inline Vec		prodVec(Vec const& right) const
	{
		Vec			result;

		if (N == 3)
		{
			result._vec[0] = _vec[1] * right._vec[2] - _vec[2] * right._vec[1];
			result._vec[1] = _vec[2] * right._vec[0] - _vec[0] * right._vec[2];
			result._vec[2] = _vec[0] * right._vec[1] - _vec[1] * right._vec[0];
		}
		return result;
	}

	inline void		normalize()
	{
		double		vecLength = this->length();

		for (unsigned int c = 0; c < N; c++)
		{
			_vec[c] = _vec[c] / vecLength;
		}
	}

protected:
	T		_vec[N];
};

typedef Vec<2, float>	Vec2f;
typedef Vec<3, float>	Vec3f;
typedef Vec<4, float>	Vec4f;

#endif // !Vec_HPP_
